import "xterm/css/xterm.css";
import "./style.css";

import { onMount, onCleanup } from "solid-js";
import { V86 } from "@/lib/v86";
import v86WasmPath from "@/lib/v86/v86.wasm?url";
import { Terminal } from "xterm";
import { FitAddon } from "xterm-addon-fit";
import { dump_file } from "@/utils";
import * as monaco from "monaco-editor";
import {
  compressToEncodedURIComponent,
  decompressFromEncodedURIComponent,
} from "lz-string";
import { codePreset } from "./data";

let code = codePreset;

const v86Config: V86Options = {
  wasm_path: v86WasmPath,
  memory_size: 128 * 1024 * 1024,
  vga_memory_size: 2 * 1024 * 1024,
  bios: { url: "./vm/seabios.bin" },
  vga_bios: { url: "./vm/vgabios.bin" },
  cdrom: {
    url: "./vm/v86-linux.iso",
  },
  filesystem: {},
  autostart: true,
  network_relay_url: "wss://relay.widgetry.org/",
  mac_address_translation: true,
  cmdline: "tsc=reliable mitigations=off random.trust_cpu=on",
  disable_keyboard: true,
  disable_mouse: true,
  disable_speaker: true,
};

function createV86SerialAddon(emulator: V86, serialName = "serial0") {
  let disposed = false;
  /**
   * @param {Terminal} term
   */
  function activate(term: Terminal) {
    emulator.add_listener(
      serialName + "-output-byte",
      function (byte: Uint8Array[number]) {
        term.write(Uint8Array.from([byte]));
      }
    );
    term.onData(function (data) {
      if (disposed) return;
      (emulator as any)[serialName + "_send"](data);
    });
  }
  function dispose() {
    disposed = true;
  }

  return {
    activate,
    dispose,
  };
}

function Main() {
  const emulator = new V86(v86Config);
  (window as any).emulator = emulator;
  const term = new Terminal({ cursorBlink: true, rows: 16 });
  (window as any).term = term;
  let editor: monaco.editor.IStandaloneCodeEditor = {} as any;
  let downloadNameInput: undefined | HTMLInputElement;

  function parseHash() {
    const hash = location.hash;
    const prefix = "#code/";
    if (hash.startsWith(prefix)) {
      code = decompressFromEncodedURIComponent(hash.slice(prefix.length));
      location.hash = "";
    }
  }
  parseHash();

  emulator.add_listener("emulator-ready", async function () {
    emulator.mount_fs("/editor");
  });

  const fitAddon = new FitAddon();
  term.loadAddon(fitAddon);
  term.loadAddon(createV86SerialAddon(emulator));

  async function clickRun() {
    const code = editor.getValue();
    const encoder = new TextEncoder();

    const buffer = encoder.encode(code);

    await emulator.create_file("/editor/main.c", buffer);
    const INCLUDES = "-I/root/tobudos/include";
    const LIBS = "-lpthread -L/root/tobudos -ltobudos";
    emulator.serial0_send(
      `gcc ${INCLUDES} /mnt/editor/main.c ${LIBS} > null && ./a.out\n`
    );
  }

  async function clickStop() {
    emulator.serial0_send("\u0003");
  }
  async function clickShare() {
    const code = editor.getValue();
    const link =
      location.origin +
      location.pathname +
      "#code/" +
      compressToEncodedURIComponent(code);
    await navigator.clipboard.writeText(link);
    window.alert("Share link copied to clipboard");
  }

  async function onUploadFile(e: any) {
    const file = e.currentTarget.files[0];
    e.currentTarget.value = "";

    const link = URL.createObjectURL(file);
    try {
      const res = await fetch(link);
      const buffer = Uint8Array.from((await res.arrayBuffer()) as any);
      await emulator.create_file("/editor/" + file.name, buffer);
    } finally {
      URL.revokeObjectURL(link);
    }
  }

  async function onSaveFile() {
    if (!downloadNameInput) return;
    const name = downloadNameInput.value;
    if (!name) return;
    console.log("save file", name);
    const result = await emulator.read_file(name);
    const filenameSplit = name.replace(/\/$/, "").split("/");
    const filename = filenameSplit[filenameSplit.length - 1] || "root";
    dump_file(result, filename);
  }

  onMount(() => {
    editor = monaco.editor.create(document.getElementById("monaco-editor")!, {
      value: code,
      language: "cpp",
      theme: "vs-dark",
      automaticLayout: true,
    });

    term.open(document.getElementById("terminal")!);
    fitAddon.fit();

    window.addEventListener("resize", () => {
      fitAddon.fit();
    });

    term.write("Welcome to Tobudos Playground!\r\n");
    term.write("booting...\r\n");

    onCleanup(() => {});
  });

  return (
    <>
      <div
        style={{ display: "flex", "flex-direction": "column", height: "100%" }}
        class={"monaco-component"}
      >
        <div></div>
        <div
          style={{
            flex: "1",
            display: "flex",
            overflow: "hidden",
          }}
        >
          <div
            style={{
              width: "180px",
              "flex-shrink": 0,
              padding: "12px",
            }}
          >
            <div>
              <button onClick={clickRun}>run</button>
            </div>
            <div>
              <button onClick={clickStop}>stop</button>
            </div>
            <div>
              <button onClick={clickShare}>share</button>
            </div>
            {/* <div>
              <input type="file" value={""} onChange={onUploadFile} />
            </div>
            <div>
              <input ref={downloadNameInput} />
              <button onClick={onSaveFile}>save</button>
            </div> */}
          </div>
          <div
            style={{ flex: "1", overflow: "hidden" }}
            id="monaco-editor"
          ></div>
        </div>
        <div style={{ width: "100%", height: "288px" }}>
          <div id="terminal"></div>
        </div>
      </div>
    </>
  );
}

export default Main;
